# -*- coding: utf-8 -*-

from abc import ABCMeta, abstractmethod
from collections import defaultdict
from .event import Event, EVENT
from .logger import get_logger

class Portfolio(object):
    __metaclass__ = ABCMeta

    @abstractmethod
    def update_signal(self, event):
        raise NotImplementedError("implement update signal")

    @abstractmethod
    def update_fill(self, event):
        raise NotImplementedError("update_fill")

# 维护一些自己的投资组合的数据
class BasicPortfolio(Portfolio):
    def __init__(self, event_hub, bars, start_date, initial_capital=1.0e5):
        self.bars = bars
        self.event_hub = event_hub
        self.symbol_list = self.bars.symbol_list
        self.start_date = start_date
        self.current_datetime = start_date
        self.initial_capital = initial_capital

        # all_positions 保存所有股票每天的仓位
        self.all_positions = self.construct_all_positions()
        # self.current_positions = {s:0 for s in self.symbol_list}
        # current_positions 保存当天的股票的仓位. 每笔交易的时候会实时变化.
        # 使用的是defaultdict, 当字典中没有数据的时候返回0.0, 为了保证新加入股票的时候不会出现错误
        self.current_positions = defaultdict(float)

        # all_holdings 保存的是每天所有股票换算成现金的价值. 还保存着那天的现金, 手续费等
        self.all_holdings = self.construct_all_holdings()
        #  current_holding 保存的是当天股票换算成的价值. 会随着每笔交易实时变化
        self.current_holdings = self.construct_current_holdings()

        # 保存着所有的signal和所有的order. 便于之后画图
        self.all_signals = []
        self.all_trades = []

    def construct_all_positions(self):
        d = {s:0 for s in self.symbol_list}
        d['datetime'] = self.start_date
        return [d]

    def construct_all_holdings(self):
        d = {s:0 for s in self.symbol_list}
        d['datetime'] = self.start_date
        d['cash'] = self.initial_capital
        d['commission'] = 0.0
        d['total'] = self.initial_capital
        return [d]

    def construct_current_holdings(self):
        # d = {s:0 for s in self.symbol_list}
        d = defaultdict(float)
        d['datetime'] = self.start_date
        d['cash'] = self.initial_capital
        d['commission'] = 0.0
        d['total'] = self.initial_capital
        return d

    def update_timeindex(self, event):
        # 更新每天的 all_holdings 和 all_postions 数据
        bars = {}

        for sym in self.symbol_list:
            bar = self.bars.get_latest_bars(sym, N=1)
            if bar != []:
                bars[sym] = bar

        self.current_datetime = bars[self.symbol_list[0]][0][1]

        dp = {s:0 for s in self.symbol_list}
        dp['datetime'] = self.current_datetime

        for s in self.symbol_list:
            dp[s] = self.current_positions[s]

        self.all_positions.append(dp)
        get_logger().info("symbol positions: " + str(dp))

        dh = {s:0 for s in self.symbol_list}
        dh['datetime'] = self.current_datetime
        dh['cash'] = self.current_holdings['cash']
        dh['commission'] = self.current_holdings['commission']
        dh['total'] = self.current_holdings['cash']
        for s in self.symbol_list:
            if s not in bars:
                # 避免没买的情况
                continue
            market_value = self.current_positions[s] * bars[s][0][5]
            dh[s] = market_value
            dh['total'] += market_value

        self.all_holdings.append(dh)

    def generate_order(self, signal):
        # 生成order
        # order event 的第二个参数实际上应该是限价单之类的, 但是现在还没考虑怎么处理
        # 现在只处理买, 卖和空仓
        if signal.signal_type == "EXIT":
            amount = self.current_positions[signal.symbol]
            order = Event(EVENT.RAW_ORDER, symbol=signal.symbol, style=signal.style, quantity=amount, direction="SELL")
        else:
            if signal.amount > 0:
                order = Event(EVENT.RAW_ORDER, symbol=signal.symbol, style=signal.style, quantity=signal.amount,
                              direction="BUY")
            else:
                order = Event(EVENT.RAW_ORDER, symbol=signal.symbol, style=signal.style, quantity=-signal.amount,
                              direction="SELL")
        return order

    def record_current_signal(self, signal):
        # 记录所有的signal, 之后画图用
        current_signal = {}
        current_signal['symbol'] = signal.symbol
        current_signal['datetime'] = signal.datetime
        current_signal['order_style'] = signal.style.get_style()
        current_signal['signal_type'] = signal.signal_type
        current_signal['amount'] = signal.amount

        self.all_signals.append(current_signal)

    def update_signal(self, event):
        # 处理signal.
        self.record_current_signal(event)
        order_event = self.generate_order(event)
        self.event_hub.publish_event(order_event)

    def update_positions_from_fill(self, fill):
        # 处理交易.会更新current_positions
        fill_dir = 0
        if fill.direction == 'BUY':
            fill_dir = 1
        if fill.direction == 'SELL':
            fill_dir = -1

        self.current_positions[fill.symbol] += fill_dir * fill.quantity

    def update_holdings_from_fill(self, fill):
        # 处理交易, 更新current_holdings
        fill_dir = 0
        if fill.direction == 'BUY':
            fill_dir = 1
        if fill.direction == 'SELL':
            fill_dir = -1

        fill_price = fill.fill_price
        cost = fill_dir * fill_price * fill.quantity

        self.current_holdings[fill.symbol] += cost
        self.current_holdings['commission'] += fill.commission
        self.current_holdings['cash'] -= (cost + fill.commission)
        self.current_holdings['total'] -= fill.commission

    def record_trades_from_fill(self, fill):
        # 记录交易, 画图用
        current_trade = {}
        current_trade['datetime'] = fill.timeindex
        current_trade['symbol'] = fill.symbol
        current_trade['exchange'] = fill.exchange
        current_trade['quantity'] = fill.quantity
        current_trade['direction'] = fill.direction
        current_trade['fill_price'] = fill.fill_price
        current_trade['commission'] = fill.commission

        self.all_trades.append(current_trade)

    def update_fill(self, event):
        # 处理交易事件
        self.update_positions_from_fill(event)
        self.update_holdings_from_fill(event)
        self.record_trades_from_fill(event)









